import base64
import json

from django.shortcuts import render
from django.views import View

# Create your views here.
"""
1.京东 登录用户可以实现购物车 未登录用户也可实现购物车
  淘宝 必须是登录用户才可以实现购物车
  
2. 登录用户数据保存  服务器  mysql/redis
                         mysql
                         redis
                         mysql+redis
   未登录用户数据保存 客户端 cookie
   
3.保存的数据   redis:
                    user_id sku_id(商品id) count(数量) selected(选中状态)
             cookie:
                    sku_id(商品id) count(数量) selected(选中状态)
4.数据的组织
    redis:   redis的数据保存在内存中 尽量少的占用redis的空间
        user_id:
                sku_id:count
                xx_selected:True/False
        1:
            1:10
            xx_1:True
            2:20
            xx_2:False
            3:30
            xx_3:True
        ####
        user_id:
                sku_id:count
                
        xx_selected:[1,3]    
        user_1: id:数量
                1:10
                2:20
                3:30
        记录选中的商品
        1,3
        #####
        user_id:
                sku_id:(-)count
        user_1:
                1:10
                2:-20
                3:30
                
                
    coolie:
            {
                sku_id:{count:xxx,selected:xxx},
                sku_id:{count:xxx,selected:xxx},
                sku_id:{count:xxx,selected:xxx},
            }     
            
5.
         cookie 字典转换为字符串保存起来  数据没有加密
         base64
         1G=1024MB
         1MB=1024KB
         1KB=1024B
         1B=8bytes       
         bytes 0 或者 1 
         ASCII
         
         base64模块使用：
            base64.b64encode()将bytes类型数据进行base64编码，返回编码后的bytes类型数据。
            base64.b64deocde()将base64编码后的bytes类型数据进行解码，返回解码后的bytes类型数据。
         
         字典-->pickle-->二进制-->Base64编码
         
         pickle模块使用：
            pickle.dumps()将Python数据序列化为bytes类型数据。
            pickle.loads()将bytes类型数据反序列化为python数据。
#字典
carts = {
    '1':{'count':1,'selected':True},
    '2':{'count':2,'selected':False},
}
#字典转换为bytes类型
import pickle
b=pickle.dumps(carts)
#############base64编码##########################
#对bytes类型的数据进行base64编码
import base64
encode=base64.b64encode(b)

#############base64解码##########################
#将base64编写的数据解码
decode_bytes=base64.b64decode(encode)
#再将解码的数据转换为字典
pickle.loads(decode_bytes)
"""
from apps.goods.models import SKU
import base64
from django.http import JsonResponse,HttpResponseBadRequest
from django_redis import get_redis_connection
import pickle
class CartsView(View):
    """
    前端：
        我们点击添加购物车后 前端将商品id发送给后端
    后端:
        请求：     接收参数，验证参数
        业务逻辑   根据商品id查询数据库 查看商品id是否正确
                  数据入库
                    登录用户入redis
                        连接reis
                        获取用户id
                        hash
                        set
                        返回响应
                    未登录用户入cookie
                        先有cookie字典
                        字典转换为bytes
                        bytes类型数据 base
                        设置cookie
                        返回响应
        响应         返回JSON
        路由         POST  /carts/
        步骤
                 1.接收数据
                 2.验证数据
                 3.判断用户登录状态
                 4.登录用户保存redis
                    4.1连接reis
                    4.2获取用户id
                    4.3hash
                    4.4set
                    4.5返回响应
                 5.未登录用户保存cookie
                    5.1先有cookie字典
                    5.2字典转换为bytes
                    5.3bytes类型数据 base
                    5.4设置cookie
                    5.5返回响应
    """
    def post(self,request):
        # 1.接收数据
        data=json.loads(request.body.decode())
        sku_id=data.get('sku_id')
        count=data.get('count')
        # 2.验证数据
        try:
            sku=SKU.objects.get(id=sku_id)
        except SKU.DoesNotExist:
            return JsonResponse({'code':400,'errmsg':'查无此商品'})
        #类型强制转换
        try:
            count = int(count)
        except Exception:
            count=1
        # 3.判断用户登录状态
        #request.user如果是登录用户 就是关联User的模型数据
        #如果不是登录用户就是匿名用户
        #匿名用户的 is_authenticated = False
        #is_authenticated认证用户
        user = request.user
        if user.is_authenticated:
            # 4.登录用户保存redis
            #    4.1连接reis
            redis_cli=get_redis_connection('carts')

            pipeline=redis_cli.pipeline()
            #    4.2获取用户id
            #    4.3hash
                #先获取之前的数据然后累加
            # redis_cli.hset('carts_%s'%user.id,sku_id,count)
            #会进行累加操作 hincrby
            pipeline.hincrby('carts_%s'%user.id,sku_id,count)

            #    4.4set
            #默认选中
            pipeline.sadd('selected_%s'%user.id,sku_id)
            #一定要执行
            pipeline.execute()
            #    4.5返回响应
            return JsonResponse({'code':0,'errmsg':'ok'})
        else:
            # 5.未登录用户保存cookie
            #5.0 先读取cookie数据
            cookie_carts=request.COOKIES.get('carts')
            if cookie_carts:
                #对数据解密
                carts=pickle.loads(base64.b64decode(cookie_carts))
            else:
                #    5.1先有cookie字典
                carts={}
            #判断新增的商品 有没有在购物车
            if sku_id in carts:
                #购物车中已经有该商品id
                #数量累计增加
                orgin_count = carts[sku_id]['count']
                count+=orgin_count
            carts[sku_id]={
                'count':count,
                'selected':True
            }
            #    5.2字典转换为bytes
            carts_bytes=pickle.dumps(carts)
            #    5.3bytes类型数据 base
            base64encode = base64.b64encode(carts_bytes)
            #    5.4设置cookie
            response = JsonResponse({'code':0,'errmsg':'ok'})
            #    5.5返回响应
            response.set_cookie('carts',base64encode.decode(),max_age=3600*24*12)
            return response

    """
    购物车页面的展示
    1、判断用户是否登录
    2、登录用户查询redis
        2.1连接redis
        2.2hash
        2.3set
        2.4遍历判断
        2.5根据商品id查询商品信息
        2.6将对象 数据转换成字典数据
        2.7返回响应
    3、未登录用户查询cookie
        3.1返回cookie数据
        3.2判断购物车是否有数据
            如果有解码 
            如果没有 初始化空字典
        3.3根据商品id查询商品信息
        3.4将对象 数据转换成字典数据
        3.5返回响应
    """
    def get(self,request):
        """展示购物车"""
        # user = request.user
        # if user.is_authenticated:
        #     redis_conn=get_redis_connection('carts')
        #     redis_carts = redis_conn.hgetall('carts_%s' % user.id)
        #     cart_selected = redis_conn.smembers('selected%s' % user.id)
        #     cart_dict={}
        #     for sku_id,count in redis_carts.items():
        #         cart_dict[int(sku_id)]={
        #             'count': int(count),
        #             'selected': sku_id in cart_selected
        #         }
        user = request.user
        if user.is_authenticated:
            # 用户已登录，查询redis购物车
            redis_conn = get_redis_connection('carts')
            # 获取redis中的购物车数据
            redis_cart = redis_conn.hgetall('carts_%s' % user.id)
            # 获取redis中的选中状态
            cart_selected = redis_conn.smembers('selected_%s' % user.id)
            # 将redis中的数据构造成跟cookie中的格式一致，方便统一查询
            cart_dict = {}
            for sku_id, count in redis_cart.items():
                cart_dict[int(sku_id)] = {
                    'count': int(count),
                    'selected': sku_id in cart_selected
                }
        else:
            # 用户未登录，查询cookies购物车
            cart_str = request.COOKIES.get('carts')
            if cart_str is not None:
                # 将cart_str转成bytes,再将bytes转成base64的bytes,最后将bytes转字典
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                cart_dict = {}
        # 构造购物车渲染数据
        # 根据商品id查询商品信息
        #可以直接遍历
        #也可以获取 字典的最外层的key 最外层的所有key就是商品id
        sku_ids = cart_dict.keys()
        skus = SKU.objects.filter(id__in=sku_ids)
        cart_skus_list = []
        for sku in skus:
            cart_skus_list.append({
                'id': sku.id,
                'name': sku.name,
                'count': cart_dict.get(sku.id).get('count'),
                'selected': str(cart_dict.get(sku.id).get('selected')),  # 将True，转'True'，方便json解析
                'default_image_url': sku.default_image.url,
                'price': str(sku.price),  # 从Decimal('10.2')中取出'10.2'，方便json解析
                'amount': str(sku.price * cart_dict.get(sku.id).get('count')),
            })
        return JsonResponse({'code': 0, 'errmsg': 'OK', 'cart_skus': cart_skus_list})

    def put(self, request):
        """修改购物车"""
        # 接收参数
        json_dict = json.loads(request.body.decode())
        sku_id = json_dict.get('sku_id')
        count = json_dict.get('count')
        selected = json_dict.get('selected', True)

        # 判断参数是否齐全
        if not all([sku_id, count]):
            return HttpResponseBadRequest('缺少必传参数')
        # 判断sku_id是否存在
        try:
            sku = SKU.objects.get(id=sku_id)
        except SKU.DoesNotExist:
            return HttpResponseBadRequest('商品sku_id不存在')
        # 判断count是否为数字
        try:
            count = int(count)
        except Exception:
            return HttpResponseBadRequest('参数count有误')

        # 判断用户是否登录
        user = request.user
        if user.is_authenticated:
            # 用户已登录，修改redis购物车
            redis_conn = get_redis_connection('carts')
            pl = redis_conn.pipeline()
            # 因为接口设计为幂等的，直接覆盖
            pl.hset('carts_%s' % user.id, sku_id, count)
            # 是否选中
            if selected:
                pl.sadd('selected_%s' % user.id, sku_id)
            else:
                pl.srem('selected_%s' % user.id, sku_id)
            pl.execute()

            # 创建响应对象
            cart_sku = {
                'id': sku_id,
                'count': count,
                'selected': selected,
                'name': sku.name,
                'default_image_url': sku.default_image.url,
                'price': sku.price,
                'amount': sku.price * count,
            }
            return JsonResponse({'code': 0, 'errmsg': '修改购物车成功', 'cart_sku': cart_sku})
        else:
            # 用户未登录，修改cookie购物车
            cart_str = request.COOKIES.get('carts')
            if cart_str:
                # 将cart_str转成bytes,再将bytes转成base64的bytes,最后将bytes转字典
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                cart_dict = {}
            # 因为接口设计为幂等的，直接覆盖
            cart_dict[sku_id] = {
                'count': count,
                'selected': selected
            }
            # 将字典转成bytes,再将bytes转成base64的bytes,最后将bytes转字符串
            cookie_cart_str = base64.b64encode(pickle.dumps(cart_dict)).decode()

            # 创建响应对象
            cart_sku = {
                'id': sku_id,
                'count': count,
                'selected': selected,
                'name': sku.name,
                'default_image_url': sku.default_image.url,
                'price': sku.price,
                'amount': sku.price * count,
            }
            response = JsonResponse({'code': 0, 'errmsg': '修改购物车成功', 'cart_sku': cart_sku})
            # 响应结果并将购物车数据写入到cookie
            response.set_cookie('carts', cookie_cart_str, max_age=7 * 24 * 3600)
            return response

    def delete(self, request):
        """删除购物车"""
        # 接收参数
        json_dict = json.loads(request.body.decode())
        sku_id = json_dict.get('sku_id')

        # 判断sku_id是否存在
        try:
            SKU.objects.get(id=sku_id)
        except SKU.DoesNotExist:
            return HttpResponseBadRequest('商品不存在')

        # 判断用户是否登录
        user = request.user
        if user is not None and user.is_authenticated:
            # 用户未登录，删除redis购物车
            redis_conn = get_redis_connection('carts')
            pl = redis_conn.pipeline()
            # 删除键，就等价于删除了整条记录
            pl.hdel('carts_%s' % user.id, sku_id)
            pl.srem('selected_%s' % user.id, sku_id)
            pl.execute()

            # 删除结束后，没有响应的数据，只需要响应状态码即可
            return JsonResponse({'code': 0, 'errmsg': '删除购物车成功'})
        else:
            # 用户未登录，删除cookie购物车
            cart_str = request.COOKIES.get('carts')
            if cart_str:
                # 将cart_str转成bytes,再将bytes转成base64的bytes,最后将bytes转字典
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                cart_dict = {}

            # 创建响应对象
            response = JsonResponse({'code': 0, 'errmsg': '删除购物车成功'})
            if sku_id in cart_dict:
                del cart_dict[sku_id]
                # 将字典转成bytes,再将bytes转成base64的bytes,最后将bytes转字符串
                cookie_cart_str = base64.b64encode(pickle.dumps(cart_dict)).decode()
                # 响应结果并将购物车数据写入到cookie
                response.set_cookie('carts', cookie_cart_str, max_age=7 * 24 * 3600)
            return response

# CartsSelectAllView
class CartsSimpleView(View):
    """商品页面右上角购物车"""

    def get(self, request):
        # 判断用户是否登录
        user = request.user
        if user.is_authenticated:
            # 用户已登录，查询Redis购物车
            redis_conn = get_redis_connection('carts')
            redis_cart = redis_conn.hgetall('carts_%s' % user.id)
            cart_selected = redis_conn.smembers('selected_%s' % user.id)
            # 将redis中的两个数据统一格式，跟cookie中的格式一致，方便统一查询
            cart_dict = {}
            for sku_id, count in redis_cart.items():
                cart_dict[int(sku_id)] = {
                    'count': int(count),
                    'selected': sku_id in cart_selected
                }
        else:
            # 用户未登录，查询cookie购物车
            cart_str = request.COOKIES.get('carts')
            if cart_str:
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                cart_dict = {}
        # 构造简单购物车JSON数据
        cart_skus = []
        sku_ids = cart_dict.keys()
        skus = SKU.objects.filter(id__in=sku_ids)
        for sku in skus:
            cart_skus.append({
                'id': sku.id,
                'name': sku.name,
                'count': cart_dict.get(sku.id).get('count'),
                'default_image_url': sku.default_image.url
            })

        # 响应json列表数据
        return JsonResponse({'code': 0, 'errmsg': 'OK', 'cart_skus': cart_skus})


class CartsSelectAllView(View):
    """全选购物车"""

    def put(self, request):
        # 接收参数
        json_dict = json.loads(request.body.decode())
        selected = json_dict.get('selected', True)

        # 校验参数
        if selected:
            if not isinstance(selected, bool):
                return HttpResponseBadRequest('参数selected有误')

        # 判断用户是否登录
        user = request.user
        if user is not None and user.is_authenticated:
            # 用户已登录，操作redis购物车
            redis_conn = get_redis_connection('carts')
            cart = redis_conn.hgetall('carts_%s' % user.id)
            sku_id_list = cart.keys()
            if selected:
                # 全选
                redis_conn.sadd('selected_%s' % user.id, *sku_id_list)
            else:
                # 取消全选
                redis_conn.srem('selected_%s' % user.id, *sku_id_list)
            return JsonResponse({'code': 0, 'errmsg': '全选购物车成功'})
        else:
            # 用户已登录，操作cookie购物车
            cart = request.COOKIES.get('carts')
            response = JsonResponse({'code': 0, 'errmsg': '全选购物车成功'})
            if cart is not None:
                cart = pickle.loads(base64.b64decode(cart.encode()))
                for sku_id in cart:
                    cart[sku_id]['selected'] = selected
                cookie_cart = base64.b64encode(pickle.dumps(cart)).decode()
                response.set_cookie('carts', cookie_cart, max_age=7 * 24 * 3600)

            return response